﻿/*
LiteGrid is a lightweght grid framework for jQuery.  It is designed to be
easily extensible, allowing any functionality to be plugged in with minimal
code.

Author: MBH
DateAuthored: 7/11/09  
*/

//The console object is provided by FireBug for Firefox.  If it doesn't exist, create
//a dummy since liteGrid logs a lot of information. 
if (!console) {
	var console = { log: function() { }, error: function() { }, warn: function() { }, info: function() { } };
}

(function($) {

	// Declare namespace if not already defined
	if (!$.inrad) {
		$.inrad = new Object();
	}

	//This is the actual liteGrid plug-in class.
	$.inrad.liteGrid = function(el, options) {
		// To avoid scope issues, use 'base' instead of 'this'
		// to reference this class from internal events and functions.
		var base = this;

		// Access to jQuery and DOM versions of element
		base.$el = $(el);
		base.el = el;

		// Add a reverse reference to the DOM object
		base.$el.data("inrad.liteGrid", base);

		//This actually performs the initialization.
		base.init = function() {

			base.options = $.extend({}, $.inrad.liteGrid.defaultOptions, options);

			//Initialize the data provider
			base.dataProvider = base.options.dataProviderFactory();

			if (base.dataProvider.initialize) {
				base.dataProvider.initialize(base, base.options);
			}

			//Initialize the layout manager, and extend 'this' with the manager.
			var layoutProvider = base.options.layoutProviderFactory();

			if (layoutProvider.initialize) {
				layoutProvider.initialize(base, base.options);

				$.extend(this, layoutProvider);
			}

			//Initialize all modules!  Modules might, for example, add
			//new columns (such as the expander column), add decorators to
			//the providers, subscribe to events, or do other fun things.
			$(base.options.modules).each(function() {

				//IE will add a reference to the window object after a trailing comma in an array (stupid IE...)
				if (!this.initialize) {
					return;
				}

				//IE (of course) doesn't supprot constructor.name, so we have to handle that separately...
				if (this.constructor.name) {
					this.moduleName = this.constructor.name;
				}
				else {
					var matches = this.constructor.toString().match(/function\s*(\w+)/);

					if (matches && matches.length == 2) {
						this.moduleName = matches[1];
					}
					else {
						this.moduleName = "unknown_module";
					}
				}

				console.log("Initializing %s...", this.moduleName);

				this.initialize(base, base.options);

				console.log("Finished!");
			});

			base.$el.trigger("initializationComplete");

			base.rebind();
		}

		//Rebinds the grid to the data provider.
		base.rebind = function() {

			//Load data.  Data providers are asynchronous.
			base.dataProvider.getData(
			//Success callback
					function(data) {
						base.render(data);
					},
			//error callback
					function(request, status, error) {
						var event = $.Event("DataLoadError");
						event.request = request;
						event.status = status;
						event.error = error;
						base.raiseErrorEvent(event);
					}
				);
		}

		//Raises error events.  Modules should use this to notify clients of errors
		//so that error-handling can be handled in a generic, consistent fashion.
		base.raiseErrorEvent = function(errorEvent) {

			console.error("Error occurred.", errorEvent);

			//First, fire the specific event type.
			base.$el.trigger(errorEvent);

			//Next, change the type to the generic grid error event and re-fire it.
			var genericEvent = $.extend({}, errorEvent);
			genericEvent.type = "GridError";

			base.$el.trigger(genericEvent);
		}

		//Gets a module with the specified name (if it exists)
		base.getModule = function(name) {

			for (var i = 0; i < options.modules.length; i++) {
				if (options.modules[i].moduleName == name) {
					return options.modules[i];
				}
			}

			return null;
		}

		//***The following functions must be defined by the configured layoutProvider.  


		//Renders the actual table.  This can be overriden by modules.
		base.render = function(dataSet) {
			throw "Configured layout provider failed to override render function.";
		}

		base.renderRows = function(dataItems) {
			throw "Configured layout provider failed to override renderRows function.";
		}

		//Builds a row that can be inserted into the table.
		base.buildRow = function(dataItem) {
			throw "Configured layout provider failed to override buildRow function.";
		}

		//(Re-)Binds a row to the specified data item. All existing data in the row is wiped clean.
		base.bindRow = function(row, dataItem) {
			throw "Configured layout provider failed to override bindRow function.";
		}

		//Inserts a row for a data item after the specified row
		//that's already in the table.
		base.insertRowAfter = function(dataItem, existingRow) {
			throw "Configured layout provider failed to override insertRowAfter function.";
		}

		//Adds a new row after all other rows.
		base.appendRow = function(dataItem) {

			throw "Configured layout provider failed to override appendRow function.";
		}

		//Begins the liteGrid rendering process.
		base.init();
	}


	$.inrad.liteGrid.defaultOptions = {
		columns: [],
		dataProviderFactory: function() { return new NullDataProvider(); },
		modules: [],
		missingValue: "",
		rowIdColumn: "ID",
		layoutProviderFactory: function() { return new BasicLayoutProvider(); },
		//These are settings that are common to multiple modules.  They really don't belong
		//here, but there's no other clean way to consolidate them that I've come up with.
		deleteColumnName: "deleted"
	}


	// This is the actual plug-in function.  It creates and returns
	// a new instance of the plug-in.  
	$.fn.inrad_liteGrid = function(options) {
		return this.each(function() {
			(new $.inrad.liteGrid(this, options));
		});
	}

	// This function breaks the chain, but returns
	// the inrad.liteGrid if it has been attached to the object.
	$.fn.getinrad_liteGrid = function() {
		return this.data("inrad.liteGrid");
	}

})(jQuery);

//A data provider that returns no data.  This is the default.
function NullDataProvider() {
	this.getData = function() {
		return new Array();
	};
}